﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Super.Common
{
    /// <summary> 固定尺寸内存池.
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TValue"></typeparam>
    public class Pool<TKey, TValue>
    {
        #region 私有成员

        /// <summary> 内存池尺寸.
        /// </summary>
        private int PoolSize { get; set; }

        /// <summary> 池子数据 readOnly 是为了 lock 安全. 保证Data 地址(CLR地址,不是内存地址)不变. 其实只要代码不设置Data的值,也是安全的,这样写严谨一些.
        /// </summary>
        private readonly List<PoolElement> _data;

        /// <summary> 池中元素
        /// </summary>
        private class PoolElement
        {
            /// <summary> key
            /// </summary>
            public TKey Key { get; set; }
            /// <summary> 值
            /// </summary>
            public TValue Value { get; set; }
            /// <summary> 最后活动时刻
            /// </summary>
            public long ActiveTick { get; set; }
        }

        /// <summary> 查找元素索引 如果根据索引赋值的话 需要加线程锁 lock
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        private int FindIndex(TKey key)
        {
            //倒叙查找,性能更高.
            return _data.FindLastIndex(j => j.Key.Equals(key));
        }

        #endregion

        /// <summary> 构造
        /// </summary>
        /// <param name="poolSize">池大小 0 表示自增</param>
        public Pool(int poolSize = 0)
        {
            PoolSize = poolSize;
            _data = new List<PoolElement>(poolSize);
        }

        /// <summary> 获取或设置池中对象. 为空 则返回 default(TValue)
        /// </summary>  
        public TValue this[TKey key]
        {
            get
            {
                lock (_data)
                {
                    var v = _data.FirstOrDefault(j => j.Key.Equals(key));

                    if (v == null)
                    {
                        return default(TValue);
                    }
                    else
                    {
                        //更新元素在池中位置
                        _data.Remove(v);
                        v.ActiveTick = DateTime.Now.Ticks;
                        _data.Add(v);
                        return v.Value;
                    }
                }
            }

            set
            {
                //实例锁 ( 锁this 实例. 效果等于并性能高于 lock(this). 以下代码,另一个实例是可以访问的,不过整个程序应该只存在一个实例. 且多实例之间无影响. 故,没关系. )
                lock (_data)
                {
                    var inx = FindIndex(key);
                    if (inx < 0)
                    {
                        _data.Add(new PoolElement() { Key = key, Value = value, ActiveTick = DateTime.Now.Ticks });

                        if (_data.Count > PoolSize && PoolSize > 0)
                        {
                            _data.RemoveAt(0);
                        }
                    }
                    else
                    {
                        _data[inx].Value = value;
                    }
                }
            }
        }

        /// <summary> 池元素数量
        /// </summary>
        public int Count
        {
            get
            {
                return _data.Count;
            }
        }

        /// <summary> 池元素活性(生命周期)
        /// </summary>
        public TimeSpan PoolLeft
        {
            get { return new TimeSpan(DateTime.Now.Ticks - _data[0].ActiveTick); }
        }

        /// <summary> 添加对象 如已存在,则替换.
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        protected void Add(TKey key, TValue value)
        {
            this[key] = value;
        }

        /// <summary> 移除对象
        /// </summary>
        /// <param name="key"></param>
        public void Remove(TKey key)
        {
            lock (_data)
            {
                var v = _data.FirstOrDefault(j => j.Key.Equals(key));
                if (v != null)
                {
                    _data.Remove(v);
                }
            }
        }

        /// <summary> 尝试从池中获取一个对象,如果没有则创建并新增.
        /// </summary>
        /// <returns></returns>
        public TValue TryGet(TKey key, Func<TValue> createMethod)
        {
            if (key == null)
            {
                return createMethod();
            }

            lock (_data)
            {
                var inx = FindIndex(key);
                if (inx < 0)
                {
                    var v = createMethod();
                    this[key] = v;
                    return v;
                }
                else
                {
                    return _data[inx].Value;
                }
            }
        }
    }
}
